/*
Prints the parse tree of a Python program.

Evan Jones
ejones@uwaterloo.ca
http://evanjones.ca/

October, 2005
*/

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "pyparser.h"

struct PyASTPrintVisitor
{
	struct PyParse_Callbacks callbacks;
	int depth;
};

void printDepth( int depth )
{
	for ( int i = 0; i < depth; ++ i )
	{
		printf( "  " );
	}
}

void visitNumber( void* state, object value )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	
	assert( PyInt_Check( value ) );
	printf( "number = %d\n", PyInt_Value( value ) );
}

void visitName( void* state, identifier value, expr_context_ty context )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	
	assert( value != NULL );
	assert( PyString_Check(value) );
	
	printf( "name = '%s'\n", PyString_AsString(value) );
}

void visitString( void* state, string value )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	
	assert( value != NULL );
	assert( PyString_Check(value) );
	
	printf( "string = '%s'\n", PyString_AsString(value) );
}

void visitCall( void* state, expr_ty func, asdl_seq *args, asdl_seq *keywords, expr_ty starargs, expr_ty kwargs )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	
	assert( keywords == NULL || keywords->size == 0 );
	assert( starargs == NULL );
	assert( kwargs == NULL );
	
	printf( "call:\n" );
	printDepth( visit->depth );
	printf( " function:\n" );
	
	visit->depth += 1;
	PyParse_Expression( visit, &visit->callbacks, func );
	visit->depth -= 1;
	
	if ( args != NULL )
	{
		printDepth( visit->depth );
		printf( " args (%d):\n", args->size );
		
		visit->depth += 1;
		for ( int i = 0; i < args->size; ++ i )
		{
			PyParse_Expression( visit, &visit->callbacks, args->elements[i] );
		}
		visit->depth -= 1;
	}
}

void visitPrint( void* state, expr_ty destination, asdl_seq* values, bool newline )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	
	printf( "print\n" );
	
	if ( destination != NULL )
	{
		printDepth( visit->depth );
		printf( " destination: " );
		PyParse_Expression( visit, &visit->callbacks, destination );
	}
	
	printDepth( visit->depth );
	printf( " values:\n" );
	visit->depth += 1;
	for ( int i = 0; i < values->size; ++ i )
	{
		PyParse_Expression( visit, &visit->callbacks, values->elements[i] );
	}
	visit->depth -= 1;
	
	if ( ! newline )
	{
		printDepth( visit->depth );
		printf( " newline: suppressed\n" );
	}
}

void visitReturn( void* state, expr_ty value )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	
	printf( "return value:\n" );
	
	visit->depth += 1;
	PyParse_Expression( visit, &visit->callbacks, value );
	visit->depth -= 1;
}

void visitAssign( void* state, asdl_seq* targets, expr_ty value )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
		
	assert( targets != NULL );
	assert( targets->size > 0 );
	assert( value != NULL );
	
	printf( "assignment\n" );
	
	printDepth( visit->depth );
	printf( " targets:\n" );
	visit->depth += 1;
	for ( int i = 0; i < targets->size; ++ i )
	{
		PyParse_Expression( visit, &visit->callbacks, targets->elements[i] );
	}
	visit->depth -= 1;
	
	printDepth( visit->depth );
	printf( " value:\n" );
	
	visit->depth += 1;
	PyParse_Expression( visit, &visit->callbacks, value );
	visit->depth -= 1;
}

void visitFunction( void* state, PyObject* name, arguments_ty arguments, asdl_seq* body, asdl_seq* decorators )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );

	assert( name->type == PY_STRING );
	assert( PyString_Check(name) );
	assert( arguments != NULL );
	assert( body != NULL );
	assert( decorators == NULL ); // Don't currently support decorators
	
	printf( "function name = '%s'\n", PyString_AsString(name) );
	if ( arguments->args != NULL )
	{
		assert( arguments->args->size > 0 );
		
		printDepth( visit->depth );
		printf( " arguments:\n" );
		
		int defaultsStartAt = -1;
		
		if ( arguments->defaults != NULL )
		{
			assert( arguments->args->size > arguments->defaults->size  );
			defaultsStartAt = arguments->args->size - arguments->defaults->size;
		}
		
		visit->depth += 1;
		// If there are arguments, print them out
		for (int i = 0; i < arguments->args->size; ++ i )
		{
			PyParse_Expression( visit, &visit->callbacks, arguments->args->elements[i] );
			if ( defaultsStartAt == i )
			{
				// This value has a defeault
				printDepth( visit->depth );
				printf( " default value:\n" );
				visit->depth += 1;
				PyParse_Expression( visit, &visit->callbacks, arguments->defaults->elements[i-defaultsStartAt] );
				visit->depth -= 1;
			}
		}
		visit->depth -= 1;
	}
	else
	{
		assert( arguments->defaults == NULL );
	}
	
	if ( arguments->vararg != NULL )
	{
		printDepth( visit->depth );
		printf( " varargs name = '%s'\n", PyString_AsString( arguments->vararg ) );
        }
	
	if ( arguments->kwarg != NULL )
	{
		printDepth( visit->depth );
		printf( " keyword args name = '%s'\n", PyString_AsString( arguments->kwarg ) );
        }
	
	if ( decorators != NULL )
	{
		printDepth( visit->depth );
		printf( " decorators = %p %d\n", decorators, decorators->size );
	}
	
	printDepth( visit->depth );
	printf( " body:\n" );
	visit->depth += 1;
	PyParse_Statements( visit, &visit->callbacks, body );
	visit->depth -= 1;
}

void visitExpression( void* state, expr_ty expression )
{
	struct PyASTPrintVisitor* visit = state;
	printDepth( visit->depth );
	printf( "expression:\n" );
	
	visit->depth += 1;
	PyParse_Expression( visit, &visit->callbacks, expression );
	visit->depth -= 1;
}


int main( int argc, const char* argv[] )
{
	if ( argc != 2 )
	{
		fprintf( stderr, "pyparse [filename]\n" );
		return 1;
	}
	
	const char* filename = argv[1];
	FILE* handle = fopen( filename, "r" );
	if ( handle == NULL )
	{
		fprintf( stderr, "Error opening file '%s'\n", filename );
		return 2;
	}
	
	mod_ty mod = PyASTFromFile( handle, filename );
	fclose( handle );
	handle = NULL;
	
	if ( mod != NULL )
	{
		assert( mod->kind == Module_kind );
		
		asdl_seq* sequence = mod->v.Module.body;
		
		struct PyASTPrintVisitor visitor;
		memset( &visitor, 0, sizeof(visitor) );
		//~ visitor.depth = 0;
		
		visitor.callbacks.function = visitFunction;
		visitor.callbacks.callback_return = visitReturn;
		visitor.callbacks.print = visitPrint;
		visitor.callbacks.assign = visitAssign;
		visitor.callbacks.expression = visitExpression;
		
		visitor.callbacks.name = visitName;
		visitor.callbacks.number = visitNumber;
		visitor.callbacks.string = visitString;
		visitor.callbacks.call = visitCall;
		
		PyParse_Statements( &visitor, &visitor.callbacks, sequence );
		
		free_mod( mod );
		mod = NULL;
	}		
	else
	{
		printf( "Parse Error?\n" );
		return 1;
		//~ err_input(&err);
		//~ return NULL;
	}

	return 0;
}
